package interview_service

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"peilian-api/app/global/databases/tables"
	"peilian-api/app/global/log"
	"peilian-api/app/global/variable"
	"peilian-api/app/model"
	"peilian-api/app/services/report_service"
	"peilian-api/utils/tools"
	"peilian-api/utils/websocket"
	"runtime"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

const (
	ConvBorderNum = 4 // 更换问题的边界，前 n 个问题是通用问题，后面问题是职业问题
	SocialCount   = 6 // 社招面试总题数, 通用岗位3道，岗位面试题3道
	PMCount       = 7 // 产品岗保留逻辑面试题，所以一共面7道题
	CivilCount    = 3 // 公务员 或者 通用职业 面试总题数

	MainClassify    = 1 // 主流程练习
	SpecialClassify = 2 // 专项练习

)

type Business struct {
	Session         *tables.Session
	SpecialRecord   *tables.SpecialRecord
	CurrentQuestion *tables.Question
	Criteria        map[int][]string //一共有几个维度：有几个维度就问几个问题 key 是 职业id  value 是string数组
	CurrentTagOld   int              // 当前的问题，前端传入的当前问题
	CurrentTag      int              // 当前是第几个问题,返回给前端的当前问题值
	CurrentNum      int              // 当前第几个问题
	CurrentRealQues *tables.RealQues
	SpeakTime       int   // 说话时间
	PointStruct     []int // 要点评分
	CurrentId       uint  // 当前问题的id
	CombineText     string
	Classify        uint     // 类型  1: 面试主流程 2：专项练习
	QuestionOrigin  []string // 面试来源
}

type Client struct {
	Id  string
	Ctx *gin.Context
	Bus Business

	partCacheLabels []interface{}
	Logger          *log.MyLogger
	INextQuestion
}

type VideoResult struct {
	SessionId  uint   `json:"session_id"`
	CareerId   uint   `json:"career_id"`
	QuestionId uint   `json:"question_id"`
	Total      uint   `json:"total"`
	CurrentNum uint   `json:"current_num"`
	Type       string `json:"type"`
	Video      string `json:"video"`
	Text       string `json:"text"`
}

type VideoRes struct {
	Code uint   `json:"code"`
	Type string `json:"type"`
	Data struct {
		SessionId  uint   `json:"session_id"`
		CareerId   uint   `json:"career_id"`
		Total      uint   `json:"total"`
		CurrentNum uint   `json:"current_num"`
		Type       string `json:"type"`
		Video      string `json:"video"`
		Text       string `json:"text"`
	} `json:"data"`
	Message []string `json:"message"`
}

type VideoInfo struct {
	SessionId  uint   `json:"session_id"`
	QuestionId uint   `json:"question_id"`
	CurrentNum uint   `json:"current_num"`
	Total      uint   `json:"total"`
	Video      string `json:"video"`
	PcVideo    string `json:"pc_video"`
	Text       string `json:"text"`
	Type       string `json:"type"`
}

func (c *Client) RecoverGoroutineCtx() {
	if res := recover(); res != nil {
		if fmt.Sprint(res) != "send on closed channel" {
			c.Logger.ErrorMsgF(c.Ctx, fmt.Sprintf("异常：%s", fmt.Sprint(res)))
		}
		const size = 64 << 10
		buf := make([]byte, size)
		buf = buf[:runtime.Stack(buf, false)]
		c.Logger.ErrorF(c.Ctx,
			fmt.Sprintf("[协程%d]", tools.GetGid()),
			"异常恢复, ",
			"异常内容: ", fmt.Sprint(res),
			" trace: ", string(buf),
		)
	}
}

func (c *Client) GetQuestionsByCareerId(careerId int) ([]*tables.Question, error) {
	questions := make([]*tables.Question, 100)
	mod := len(c.Bus.Criteria[careerId])
	if len(c.Bus.Criteria[int(c.Bus.Session.CareerID)]) > len(c.Bus.Criteria[careerId]) {
		mod = len(c.Bus.Criteria[int(c.Bus.Session.CareerID)])
	}
	tag := c.Bus.CurrentTag
	if careerId != tables.SelfIntroduction && careerId != tables.QCivilServant {
		tag = c.Bus.CurrentTag - 1
	}
	if err := variable.DB.MySQL.
		Where("type!=? and career_id=? and criteria=?", "extra_question", // 不是追问
			careerId, c.Bus.Criteria[careerId][tag%mod]).
		Order("id").Find(&questions).Error; err != nil {
		return nil, err
	}
	if len(questions) <= 0 {
		return nil, fmt.Errorf("[%s]这个维度没有标准问", c.Bus.Criteria[careerId][c.Bus.CurrentTag])
	}
	return questions, nil
}

func (c *Client) GetQuestions() ([]*tables.Question, error) {
	questions := make([]*tables.Question, 100)
	tag := c.Bus.CurrentTag
	// if c.Bus.Session.CareerID == 2 {
	// 	tag = c.Bus.CurrentTag
	// }

	if err := variable.DB.MySQL.
		Where("type!=? and career_id=? and criteria=?", "extra_question", // 不是追问–
			c.Bus.Session.CareerID, c.Bus.Criteria[int(c.Bus.Session.CareerID)][tag%len(c.Bus.Criteria[int(c.Bus.Session.CareerID)])]).
		Order("id").Find(&questions).Error; err != nil {
		return nil, err
	}
	if len(questions) <= 0 {
		return nil, fmt.Errorf("[%s]这个维度没有标准问", c.Bus.Criteria[int(c.Bus.Session.CareerID)][c.Bus.CurrentTag%len(c.Bus.Criteria[int(c.Bus.Session.CareerID)])])
	}
	return questions, nil
}

// 通用追问
func (c *Client) GetNoTagExtraQuestion() (*tables.Question, error) {
	// 确定选择的问题
	questions := make([]*tables.Question, 100)
	if len(c.Bus.PointStruct) == 0 || (len(c.Bus.PointStruct) != 0 && c.Bus.PointStruct[0] == 0) {
		if err := variable.DB.MySQL.
			Where("parent_text=? and career_id=?", c.Bus.CurrentQuestion.Text, c.Bus.CurrentQuestion.CareerID).
			Order("is_order asc").Find(&questions).Error; err != nil {
			return nil, err
		}
	} else {
		if err := variable.DB.MySQL.
			Where("parent_text=? and career_id=? and point_type in (?)", c.Bus.CurrentQuestion.Text, c.Bus.CurrentQuestion.CareerID, c.Bus.PointStruct).
			Order("is_order asc").Find(&questions).Error; err != nil {
			return nil, err
		}
	}

	if len(questions) == 0 {
		return nil, errors.New("没有相应的追问题目")
	}

	// quesSub := rand.Intn(len(questions))
	quesSub := 0
	return questions[quesSub], nil
}

// 猎聘追问
func (c *Client) GetExtraQuestion(keys []string) (*tables.Question, error) {
	questions := make([]*tables.Question, 100)
	if err := variable.DB.MySQL.
		Where("parent_text=? and career_id=? and tag in (?)", c.Bus.CurrentQuestion.Text, c.Bus.CurrentQuestion.CareerID, keys).
		Order("id").Find(&questions).Error; err != nil {
		return nil, err
	}
	if len(questions) == 0 {
		return nil, errors.New("没有相应的追问题目")
	}
	quesSub := rand.Intn(len(questions))
	return questions[quesSub], nil
}

func (c *Client) GetFinishVideo() (*tables.MarginVideo, error) {
	finishVideo := make([]*tables.MarginVideo, 10)
	if err := variable.DB.MySQL.
		Where("type=?", "finish").Find(&finishVideo).Error; err != nil {
		return nil, err
	}
	if len(finishVideo) <= 0 {
		return nil, errors.New("无可用的结束视频")
	}
	finishSub := rand.Intn(len(finishVideo))
	update := map[string]interface{}{
		"status":    c.getFinishedStatus(),
		"finish_at": time.Now(),
		"duration":  time.Now().Unix() - c.Bus.Session.TrainAt.Unix(),
	}
	variable.DB.MySQL.Model(&tables.Session{}).Where("id = ?", c.Bus.Session.ID).Updates(update)
	return finishVideo[finishSub], nil
}

func (c *Client) GetCurRealQues() (*tables.RealQues, error) {
	c.Bus.CurrentRealQues = new(tables.RealQues)
	if err := variable.DB.MySQL.
		Where("session_id=? and question_id=?", c.Bus.Session.ID, c.Bus.CurrentQuestion.ID).Preload("Question").
		First(c.Bus.CurrentRealQues).Error; err != nil {
		c.Logger.ErrorF(c.Ctx, "查询客户端对应的当前问题记录错误: "+err.Error())
		return nil, err
	}
	return c.Bus.CurrentRealQues, nil
}

func (c *Client) GetConvVideo(answer string) []byte {
	speakTime := c.Bus.SpeakTime
	realQues, err := c.GetCurRealQues()
	if err != nil {
		return c.ResClient("error", &VideoInfo{}, err.Error())
	}
	if realQues.Question.Criteria == "自我介绍" {
		return c.GetSelfIntroductionConv()
	}
	// 含有追问
	c.Logger.InfoF(c.Ctx, fmt.Sprintf("问题类型:%s , 缺少id: %x, 回答时长：%d", c.Bus.CurrentQuestion.Type, c.Bus.PointStruct, speakTime))
	// a) 未达到题目个数，并且该题是追问，则需要转场
	if c.Bus.CurrentTag < c.getTotal() && (c.Bus.CurrentQuestion.Type == tables.QExtra || c.Bus.CurrentQuestion.Type == tables.QNo) {
		return c.GetConvSession(realQues, answer)
	}
	// b) 未达到题目个数，并且该题是主题干，判断是否需要转场
	if c.Bus.CurrentTag < c.getTotal() {
		if len(c.Bus.PointStruct) == 0 { // 1. 题目评分是0 则不追问 进行 转场
			return c.GetConvSession(realQues, answer)
		}
		if c.Bus.PointStruct[0] == 0 && c.Bus.SpeakTime > 60 { // 2. 要点全覆盖 & 回答时长大于1min 则不追问 进行转场
			return c.GetConvSession(realQues, answer)
		}
		//b := c.NextQuestion(0)
		//if c.Bus.CurrentTag <= c.getTotal() { // 最后一道题，反而下一题了，那么就需要结束
		//	return b
		//}
	}
	// 需要调用转场
	if c.Bus.CurrentTag < c.getTotal() {
		return c.GetConvSession(realQues, answer)
	} else {
		// 面试过程 判断  面试题的个数
		// 社招面试是6道题  公务员面试是4道题  通用职业面试是4道题，索引不能通过

		// 面试完成后需要更新面试次数，未完成的面试不记录面试次数
		u, err := tables.GetUserInfoById(int(c.Bus.Session.UserID))
		if err != nil {
			return c.ResClient("error", &VideoInfo{}, err.Error())
		}
		where := make(map[string]interface{}, 0)
		where["train_count"] = u.TrainCount + 1
		//if u.Type == tables.UserTypeAnonymous && u.TrailCount > 0 {
		//	where["trail_count"] = u.TrailCount - 1
		//}
		tables.UpdateMapUserInfoById(where, int(u.ID)) // 更新面试次数
		finishVideo, err := c.GetFinishVideo()
		if err != nil {
			return c.ResClient("error", &VideoInfo{}, err.Error())
		}
		return c.ResClient("finish", &VideoInfo{
			Video:   finishVideo.Video,
			PcVideo: finishVideo.PcVideo,
			Text:    finishVideo.Text,
		})
	}
}

func (c *Client) ResClient(code string, videoInfo *VideoInfo, msg ...string) []byte {
	res := &VideoRes{
		Code: 200,
		Type: code,
		Data: struct {
			SessionId  uint   `json:"session_id"`
			CareerId   uint   `json:"career_id"`
			Total      uint   `json:"total"`
			CurrentNum uint   `json:"current_num"`
			Type       string `json:"type"`
			Video      string `json:"video"`
			Text       string `json:"text"`
		}{
			SessionId:  c.Bus.Session.ID,
			CareerId:   c.Bus.Session.CareerID,
			Total:      uint(c.getTotal()),
			CurrentNum: videoInfo.CurrentNum,
			Type:       code,
			Video:      videoInfo.Video,
			Text:       videoInfo.Text,
		},
		Message: msg,
	}
	if code == "error" {
		res.Code = 500
	}
	serialData, _ := json.Marshal(res)
	return serialData
}

func (c *Client) CreateRealQues(BelongToID *uint) []byte {
	var questionType = 0
	if c.Bus.CurrentQuestion.Type == tables.QExtra {
		questionType = 1
	}
	if c.Bus.CurrentQuestion.Type != tables.QExtra {
		c.Bus.CurrentTag += 1
	}
	realQues := tables.RealQues{
		QuestionID:     c.Bus.CurrentQuestion.ID,
		SessionID:      c.Bus.Session.ID,
		QuestionType:   uint(questionType),
		QuestionText:   c.Bus.CurrentQuestion.Text,
		CurrentTag:     uint(c.Bus.CurrentTag),
		BelongToID:     BelongToID,
		QuestionOrigin: c.Bus.CurrentQuestion.QuestionOrigin,
		AnsAnalysisTag: c.Bus.CurrentQuestion.AnsAnalysisTag,
	}

	if err := variable.DB.MySQL.Select("QuestionID", "SessionID", "QuestionType", "QuestionText", "CurrentTag", "BelongToID", "QuestionOrigin", "AnsAnalysisTag").
		Create(&realQues).Error; err != nil {
		c.Logger.ErrorF(c.Ctx, "客户端对应的当前问题记录入库失败")
		return c.ResClient("error", &VideoInfo{}, "客户端对应的当前问题记录入库失败:"+err.Error())
	}
	c.Bus.CurrentRealQues = &realQues
	return nil
}

func (c *Client) GetConvSession(realQues *tables.RealQues, answer string) []byte {
	marginVideo, err := websocket.ConvSession(realQues, answer)
	if err != nil {
		return c.ResClient("error", &VideoInfo{}, err.Error())
	}
	return c.ResClient("conv", &VideoInfo{
		Video:   marginVideo.Video,
		PcVideo: marginVideo.PcVideo,
		Text:    marginVideo.Text,
	})
}

func (c *Client) GetSelfIntroductionConv() []byte {
	marginVideos := make([]*tables.MarginVideo, 10)
	if err := variable.DB.MySQL.
		Where("tag=? and type=?", "无", "convert").
		Find(&marginVideos).Error; err != nil {
		return c.ResClient("error", &VideoInfo{}, err.Error())
	}
	if len(marginVideos) <= 0 {
		return c.ResClient("error", &VideoInfo{}, "无可用转场视频")
	}
	randSub := rand.Intn(len(marginVideos))
	convVideo := marginVideos[randSub]
	return c.ResClient("conv", &VideoInfo{
		Video:   convVideo.Video,
		PcVideo: convVideo.PcVideo,
		Text:    convVideo.Text,
	})
}

func (c *Client) GetCurrentRealquestion(sessionId, currentNum uint) (tables.RealQues, error) {
	var rq tables.RealQues
	d := variable.DB.MySQL.Model(&tables.RealQues{}).Preload("Question").Where("session_id = ? and current_tag = ?", sessionId, currentNum+1).Find(&rq)
	if d.Error != nil {
		return rq, d.Error
	}
	if d.Error == gorm.ErrRecordNotFound {
		return rq, nil
	}
	return rq, nil
}

// 获取面试题目的第一道题
func (c *Client) GetInterviewFirstQuestion(ctx *gin.Context, ifq model.InterviewQuestionReq) VideoResult {
	var out = VideoResult{}
	sessionId := ifq.SessionId
	careerId := ifq.CareerId
	currentNum := ifq.CurrentNum
	client := InitClient(ctx, sessionId)
	if client == nil {
		return out
	}
	c = client
	criteria := c.Bus.Criteria
	var total = 0
	for _, v := range criteria {
		total += len(v)
	}
	rq, _ := c.GetCurrentRealquestion(sessionId, currentNum)
	if rq.ID != 0 {
		out.SessionId = rq.SessionID
		out.CareerId = careerId
		out.Total = uint(c.getTotal())
		out.CurrentNum = rq.CurrentTag
		out.QuestionId = rq.QuestionID
		out.Type = "ask"
		out.Video = rq.Question.Video
		out.Text = rq.Question.Text
		return out
	}

	// 获取信息
	var career = tables.Career{}
	d := variable.DB.MySQL.Model(&tables.Career{}).Where("id = ?", careerId).Find(&career)
	if d.Error != nil {
		return out
	}
	b := c.FirstQuesVideo()
	var vr = VideoRes{}
	json.Unmarshal(b, &vr)
	// 将数据进行缓存
	c.CacheInterviewClient()
	out.SessionId = c.Bus.Session.ID
	out.CareerId = c.Bus.Session.CareerID
	out.Total = uint(c.getTotal())
	out.CurrentNum = uint(c.Bus.CurrentTag)
	out.QuestionId = c.Bus.CurrentQuestion.ID
	out.Type = vr.Type
	out.Video = vr.Data.Video
	out.Text = vr.Data.Text
	return out
}

// 要点评分 返回要点评分详情，要点评分
func (c *Client) lackPointType(answerText string) (string, float64) {
	var lackPointType = make([]int, 0)
	pointText := c.Bus.CurrentQuestion.PointText
	if len(pointText) != 0 && len(answerText) > 50 { // 必须有文本才能进行评分
		var pointScore = 0.00
		var pts []tables.PointTextStruct // 要点评分数据结构
		var pointScoreDetail []tables.PointTextDetail
		_ = json.Unmarshal([]byte(pointText), &pts)
		for i := 0; i < len(pts); i++ {
			score := report_service.FetchQScore(answerText, pts[i].Value)
			pointScoreDetail = append(pointScoreDetail, tables.PointTextDetail{
				Type:  pts[i].Type,
				Label: pts[i].Label,
				Value: pts[i].Value,
				Score: score,
			})
			if score < 0.5 { // 缺少要点
				lackPointType = append(lackPointType, i+1)
			}
			pointScore += score
		}
		if len(lackPointType) == 0 { // 说明不缺少要点
			lackPointType = append(lackPointType, 0)
		}
		avgPointScore := pointScore / float64(len(pts))
		b, _ := json.Marshal(pointScoreDetail)
		c.Bus.PointStruct = lackPointType
		return string(b), avgPointScore
	}
	return "", 0.00
}

func (c *Client) FinishedReplying(ctx *gin.Context, inq model.InterviewReplyReq) VideoResult {
	sessionId := inq.SessionId
	answerText := inq.AnswerText
	answerUrl := inq.AnswerUrl
	speakTime := inq.SpeakTime
	client := InitClient(ctx, sessionId)
	c = client
	c.Bus.SpeakTime = int(speakTime)
	c.Bus.CombineText = answerText
	c.Bus.CurrentQuestion = new(tables.Question)
	c.Bus.CurrentQuestion.ID = inq.QuestionId

	rq, _ := c.GetCurRealQues()
	c.Bus.CurrentId = rq.ID

	// 更新数据
	update := make(map[string]interface{})
	update["answer_text"] = answerText
	update["speak_time"] = speakTime
	if c.Bus.Session.Career.ReportType == "pdf" {
		s, f := c.lackPointType(answerText)
		if len(s) != 0 {
			update["main_point_score_detail"] = s
			update["main_point_score"] = f
		}
	}
	update["answer_audio"] = "hbzp.pcm"
	update["audio"] = answerUrl
	if c.Bus.Session.Career.ReportType == "web" {
		go func() {
			defer c.RecoverGoroutineCtx()
			t1 := time.Now().UnixMilli()
			partLabelsData := report_service.FetchLabelsCache(c.Ctx, c.Bus.Session.Career.Name, answerText)
			if err := variable.DB.MySQL.Model(c.Bus.CurrentRealQues).
				Update("part_labels_cache", partLabelsData).Error; err != nil {
				c.Logger.ErrorF(c.Ctx, err.Error())
			}
			var partLabelsCache interface{}
			_ = json.Unmarshal([]byte(partLabelsData), &partLabelsCache)
			if c.partCacheLabels == nil {
				c.partCacheLabels = make([]interface{}, 0)
			}
			c.partCacheLabels = append(c.partCacheLabels, partLabelsCache)
			if err := c.CacheNextQues(*c); err != nil {
				c.Logger.ErrorF(c.Ctx, err.Error())
			}
			t2 := time.Now().UnixMilli()
			c.Logger.Debug(fmt.Sprintf("[recommend]标签提取及评测以及推荐下一题共使用[%d]ms", t2-t1))
		}()
	}
	variable.DB.MySQL.Model(&tables.RealQues{}).Where("id = ?", c.Bus.CurrentId).Updates(update)

	b := c.GetConvVideo(answerText)
	var vr = VideoRes{}
	json.Unmarshal(b, &vr)
	var out = VideoResult{}
	if vr.Type == "error" {
		return out
	}
	// 将数据进行缓存
	c.CacheInterviewClient()
	out.SessionId = c.Bus.Session.ID
	out.CareerId = c.Bus.Session.CareerID
	out.Total = uint(c.getTotal())
	out.CurrentNum = uint(c.Bus.CurrentTag)
	out.QuestionId = 0
	out.Type = vr.Type
	out.Video = vr.Data.Video
	out.Text = vr.Data.Text
	return out
}

// 自定义职业 回答完毕
func (c *Client) CustomFinishedReplying(ctx *gin.Context, inq model.HbCustomFinishReplay) error {
	sessionId := inq.SessionId
	answerText := inq.AnswerText
	questionId := inq.QuestionId
	questionText := inq.QuestionText
	careerName := inq.CareerName
	answerUrl := inq.AnswerUrl
	speakTime := inq.SpeakTime
	client := InitClient(ctx, sessionId)
	c = client
	c.Bus.Session.Career = &tables.Career{}
	c.Bus.Session.Career.ReportType = "custom"
	c.Bus.Session.Career.Name = careerName

	c.Bus.SpeakTime = int(speakTime)
	c.Bus.CombineText = answerText

	// 根据结果插入realQuestion数据
	q, err := new(tables.Question).GetQuestionsByWhereStr(fmt.Sprintf("id = %d", questionId))
	if err != nil || len(q) == 0 {
		return fmt.Errorf("该问题不存在，请检查question_id")
	}
	// 将数据插入到数据中
	var rq = tables.RealQues{}
	d := variable.DB.MySQL.Model(tables.RealQues{}).Where("session_id = ? and question_id = ?", sessionId, questionId).Find(&rq)
	if d.Error != nil {
		return d.Error
	}

	if rq.ID == 0 { // 说明数据不存在
		realQuestion := tables.RealQues{
			SessionID:    sessionId,
			QuestionID:   questionId,
			AnswerText:   answerText,
			AnswerAudio:  answerUrl,
			SpeakTime:    speakTime,
			QuestionText: questionText,
		}
		variable.DB.MySQL.Model(tables.RealQues{}).Save(&realQuestion)
		rq = realQuestion
	}
	c.Bus.CurrentQuestion = &q[0]

	// rq, _ := c.GetCurRealQues()
	c.Bus.CurrentId = rq.ID

	// 更新数据
	update := make(map[string]interface{})
	update["answer_text"] = answerText
	update["speak_time"] = speakTime
	if c.Bus.Session.Career.ReportType == "pdf" {
		s, f := c.lackPointType(answerText)
		if len(s) != 0 {
			update["main_point_score_detail"] = s
			update["main_point_score"] = f
		}
	}
	update["answer_audio"] = "hbzp.pcm"
	update["audio"] = answerUrl
	if c.Bus.Session.Career.ReportType == "web" {
		go func() {
			defer c.RecoverGoroutineCtx()
			t1 := time.Now().UnixMilli()
			partLabelsData := report_service.FetchLabelsCache(c.Ctx, c.Bus.Session.Career.Name, answerText)
			if err := variable.DB.MySQL.Model(c.Bus.CurrentRealQues).
				Update("part_labels_cache", partLabelsData).Error; err != nil {
				c.Logger.ErrorF(c.Ctx, err.Error())
			}
			var partLabelsCache interface{}
			_ = json.Unmarshal([]byte(partLabelsData), &partLabelsCache)
			if c.partCacheLabels == nil {
				c.partCacheLabels = make([]interface{}, 0)
			}
			c.partCacheLabels = append(c.partCacheLabels, partLabelsCache)
			if err := c.CacheNextQues(*c); err != nil {
				c.Logger.ErrorF(c.Ctx, err.Error())
			}
			t2 := time.Now().UnixMilli()
			c.Logger.Debug(fmt.Sprintf("[recommend]标签提取及评测以及推荐下一题共使用[%d]ms", t2-t1))
		}()
	}
	variable.DB.MySQL.Model(&tables.RealQues{}).Where("id = ?", c.Bus.CurrentId).Updates(update)
	// 将数据进行缓存
	// c.CacheInterviewClient()
	// out.SessionId = c.Bus.Session.ID
	// out.CareerId = c.Bus.Session.CareerID
	// out.Total = uint(c.getTotal())
	// out.CurrentNum = uint(c.Bus.CurrentTag)
	// out.QuestionId = 0
	// out.Type = vr.Type
	// out.Video = vr.Data.Video
	// out.Text = vr.Data.Text
	return nil
}

// 获取下一题
func (c *Client) GetInterviewNextQuestion(ctx *gin.Context, ifq model.InterviewQuestionReq) VideoResult {
	sessionId := ifq.SessionId
	careerId := ifq.CareerId
	currentNum := ifq.CurrentNum
	client := InitClient(ctx, sessionId)
	c = client

	var out = VideoResult{}
	rq, _ := c.GetCurrentRealquestion(sessionId, currentNum)
	if rq.ID != 0 {
		out.SessionId = rq.SessionID
		out.CareerId = careerId
		out.Total = uint(c.getTotal())
		out.CurrentNum = rq.CurrentTag
		out.QuestionId = rq.QuestionID
		out.Type = "ask"
		out.Video = rq.Question.Video
		out.Text = rq.Question.Text
		return out
	}

	c.Logger.InfoF(c.Ctx, fmt.Sprintf("msg:AUDIO::CONV::FINISH, 第[%d]道面试题", c.Bus.CurrentTag))
	b := c.NextQuestion(1)

	var vr = VideoRes{}
	json.Unmarshal(b, &vr)
	// 将数据进行缓存
	c.CacheInterviewClient()
	out.SessionId = c.Bus.Session.ID
	out.CareerId = c.Bus.Session.CareerID
	out.Total = uint(c.getTotal())
	out.CurrentNum = uint(c.Bus.CurrentTag)
	out.QuestionId = c.Bus.CurrentQuestion.ID
	out.Type = vr.Type
	out.Video = vr.Data.Video
	out.Text = vr.Data.Text
	return out
}

func (c *Client) NextQuesReqData() ([]byte, error) {
	realQues, err := c.GetCurRealQues()
	if err != nil {
		return nil, err
	}
	nextQuesReq := &NextQuesReq{
		Name: "next_question",
		Query: []struct {
			QuestionID uint   `json:"c_question_id"`
			Question   string `json:"question"`
			Answer     string `json:"answer"`
		}{{
			QuestionID: c.Bus.CurrentQuestion.ID,
			Question:   c.Bus.CurrentQuestion.Text,
			Answer:     realQues.AnswerText,
		}},
	}
	nextQuesReqData, _ := json.Marshal(nextQuesReq)
	c.Logger.InfoF(c.Ctx, "请求追问服务的数据: "+string(nextQuesReqData))
	return nextQuesReqData, nil
}

// 下一通用问题
func (c *Client) GetNextCommonQuestion() []byte {
	nextQues, err := c.GetQuestionsByCareerId(tables.QCommonId) // 下一个通用问题
	if err != nil {
		return c.ResClient("error", &VideoInfo{}, err.Error())
	}
	quesSub := rand.Intn(len(nextQues))
	c.Bus.CurrentQuestion = nextQues[quesSub]
	// c.Bus.CurrentTag += 1
	if ok := c.CreateRealQues(nil); ok != nil {
		return ok
	}
	return c.ResClient("ask", &VideoInfo{
		Video:   c.Bus.CurrentQuestion.Video,
		PcVideo: c.Bus.CurrentQuestion.PcVideo,
		Text:    c.Bus.CurrentQuestion.Text,
	})
}

func (c *Client) GetNextNormalQuestion() []byte {
	var nextQues []*tables.Question
	// 如果问题小于当前职业能力的个数，否则走通用题库
	var err error
	if c.Bus.CurrentTag-1 < len(c.Bus.Criteria[int(c.Bus.Session.CareerID)]) {
		nextQues, err = c.GetQuestions()
	} else {
		nextQues, err = c.GetQuestionsByCareerId(tables.QCommonId)
	}
	if err != nil {
		return c.ResClient("error", &VideoInfo{}, err.Error())
	}
	quesSub := rand.Intn(len(nextQues))
	c.Bus.CurrentQuestion = nextQues[quesSub]
	// c.Bus.CurrentTag += 1
	if ok := c.CreateRealQues(nil); ok != nil {
		return ok
	}
	return c.ResClient("ask", &VideoInfo{
		Video:   c.Bus.CurrentQuestion.Video,
		PcVideo: c.Bus.CurrentQuestion.PcVideo,
		Text:    c.Bus.CurrentQuestion.Text,
	})
}

type DefaultClient struct {
	*Client
}

func (c *DefaultClient) CacheNextQues(Client) error { return nil }

// flag 是 1 无需判断追问
func (c *DefaultClient) NextQuestion(flag int) []byte {
	if flag == 1 {
		return c.GetNextNormalQuestion()
	}
	quesID := strconv.Itoa(int(c.Bus.CurrentQuestion.ID))
	// 如果当前问题是 通用追问， 不需要使用猎聘的追问列表
	if c.Bus.CurrentQuestion.Type == tables.QCommon {
		// 进行追问
		c.Bus.PointStruct = append(c.Bus.PointStruct, 0) // 补个0,兼容不缺少要点的情况
		next, err := c.GetNoTagExtraQuestion()
		if err != nil {
			c.Logger.ErrorF(c.Ctx, "获取追问问题异常:"+err.Error()+";开始下一个标准问")
			return c.GetNextNormalQuestion() //下一个标准问
		}
		belongRealQues, _ := c.GetCurRealQues()
		c.Bus.CurrentQuestion = next
		if ok := c.CreateRealQues(&belongRealQues.QuestionID); ok != nil {
			return ok
		}
		return c.ResClient("extra", &VideoInfo{
			Video:   next.Video,
			PcVideo: next.PcVideo,
			Text:    next.Text,
			// QuestionOrigin: next.QuestionOrigin,
		})
	} else if c.Bus.CurrentQuestion.Type == tables.QHunter { // 问题已经不存在猎聘追问，可以废弃
		nextReqData, err := c.NextQuesReqData()
		if err != nil {
			return c.ResClient("error", &VideoInfo{}, err.Error())
		}
		resp, err := http.Post(
			variable.Config.Services.NextQuestion,
			"application/json",
			bytes.NewReader(nextReqData),
		)
		if err != nil {
			c.Logger.ErrorF(c.Ctx, "追问接口调用失败: "+err.Error())
			return c.ResClient("error", &VideoInfo{}, "追问接口调用失败: "+err.Error())
		}
		res := make(map[string]map[string]QuesInfo)
		err = tools.ReadResponseBody(resp, &res)
		if err != nil {
			errMsg := "追问接口响应结果解析失败" + err.Error()
			c.Logger.ErrorF(c.Ctx, errMsg)
			return c.ResClient("error", &VideoInfo{}, errMsg)
		}
		nextType := res["data"][quesID].Type
		keys := res["data"][quesID].Key
		if nextType == "追问" && len(keys) > 0 {
			next, err := c.GetExtraQuestion(keys)
			if err != nil {
				c.Logger.ErrorF(c.Ctx, "获取追问问题异常:"+err.Error()+";开始下一个标准问")
				return c.GetNextNormalQuestion() //下一个标准问
			}
			belongRealQues, _ := c.GetCurRealQues()
			c.Bus.CurrentQuestion = next
			if ok := c.CreateRealQues(&belongRealQues.QuestionID); ok != nil {
				return ok
			}
			return c.ResClient("extra", &VideoInfo{
				Video:   next.Video,
				PcVideo: next.PcVideo,
				Text:    next.Text,
				// QuestionOrigin: next.QuestionOrigin,
			})
		}
	}
	return c.GetNextNormalQuestion()
}

func (c *DefaultClient) getTotal() int {
	totalQuesCnt := 0
	for _, criteria := range c.Bus.Criteria {
		totalQuesCnt += len(criteria)
	}
	return totalQuesCnt // 普通社招面试题数
}

func (c *DefaultClient) FirstQuesVideo() []byte {
	// 如果 不是公务员 第一道 取当前职业能力题库
	var careerId int
	if c.Bus.Session.CareerID == tables.QCivilServant {
		careerId = int(c.Bus.Session.CareerID)
	} else {
		careerId = tables.SelfIntroduction
	}
	questions, err := c.GetQuestionsByCareerId(careerId) // 14-是自我介绍的岗位ID
	if err != nil {
		return c.ResClient("error", &VideoInfo{}, err.Error())
	}
	quesSub := rand.Intn(len(questions))
	c.Bus.CurrentQuestion = questions[quesSub]
	// c.Bus.CurrentTag += 1
	if err1 := c.CreateRealQues(nil); err1 != nil {
		return err1
	}
	return c.ResClient("ask", &VideoInfo{
		Video:   c.Bus.CurrentQuestion.Video,
		PcVideo: c.Bus.CurrentQuestion.PcVideo,
		Text:    c.Bus.CurrentQuestion.Text,
	})
}

func (c *DefaultClient) getFinishedStatus() int {
	return tables.IoStatus
}

func (c *Client) AskFinished(ctx *gin.Context, req model.AskFinishedReq) error {
	sessionId := req.SessionId
	session, err := GetSessionObj(sessionId)
	if err != nil {
		return fmt.Errorf("find sesison error:%+v", err)
	}
	if session.UserID != req.UserId {
		return fmt.Errorf("session user 对应关系有误")
	}
	var question = tables.Question{}
	if d := variable.DB.MySQL.Model(&tables.Question{}).Where("id = ?", req.QuestionId).First(&question); d.Error != nil {
		return d.Error
	}
	// 查询用户是否已经回答了问题
	var count int64
	if d := variable.DB.MySQL.Model(&tables.RealQues{}).Where("session_id = ?", sessionId).Count(&count); d.Error != nil {
		return d.Error
	}

	realRuestion := tables.RealQues{
		SessionID:      sessionId,
		QuestionID:     question.ID,
		QuestionText:   question.Text,
		CurrentTag:     uint(count) + 1,
		SpeakTime:      req.SpeakTime,
		AnswerText:     req.AnswerText,
		AnswerAudio:    "hbzp.pcm",
		Audio:          req.AnswerUrl,
		QuestionOrigin: question.QuestionOrigin,
	}

	d := variable.DB.MySQL.Create(&realRuestion)
	if d.Error != nil {
		return fmt.Errorf("插入失败:%+v", d.Error)
	}
	return nil
}
